package net.benhui.btgallery.bluelet;

import java.io.IOException;
import java.util.Vector;

import javax.bluetooth.BluetoothStateException;
import javax.bluetooth.DeviceClass;
import javax.bluetooth.DiscoveryAgent;
import javax.bluetooth.DiscoveryListener;
import javax.bluetooth.LocalDevice;
import javax.bluetooth.RemoteDevice;
import javax.bluetooth.ServiceRecord;
import javax.bluetooth.UUID;
import javax.microedition.lcdui.Display;

import uns.fit.gis.MapDrawer;

import net.benhui.btgallery.Util;

/**
 *
 * <p>Title: Bluetooth Application Utility GUI Component</p>
 * <p>Description:
 *
 * Note: This class must be used as singleton.
 * </p>
 * @author Ben Hui (www.benhui.net)
 * @version 1.0
 *
 * LICENSE:
 * This code is licensed under GPL. (See http://www.gnu.org/copyleft/gpl.html)
 */
public class BLUElet {
	//public static BLUElet instance;

	private Vector devices = new Vector();
	private Vector deviceClasses = new Vector();
	private Vector services = new Vector();
	private int selectedDevice = -1;
	private boolean inquiryCompleted = false;
	// discovery mode in device inquiry
	private int discoveryMode;
	// list of UUID to match during service discovery
	private UUID[] serviceUUIDs = null;

	// Bluetooth return code from device inquiry operation
	// see DiscoveryListener
	private int deviceReturnCode;
	// Bluetooth return code from service discovery operation
	// see DiscoveryListener
	private int serviceReturnCode;

	private LocalDevice device;
	private DiscoveryAgent agent;

	/**
	 * Creae a new BLUElet.
	 * @param host MIDlet
	 * @param listener CommandListener
	 */
	public BLUElet() {
		//this.host = host;
		//instance = this;
	}

	/**
	 * Utility function to write log message.
	 * @param s String
	 */
	public static void log(String s) {
		System.out.println(s);
	}

	/**
	 * Obtain reference to device selection screen component.
	 * You should show this screen when user invoke device search.
	 * @return Screen
	 */

	/**
	 * Get all discovered services from selected remote device.
	 * Your application call this method after your app receive COMPLETED callback
	 * event. This will return all services that match your UUIDs in startInquiry().
	 * @return ServiceRecord[]
	 */
	public ServiceRecord[] getDiscoveredServices() {
		ServiceRecord[] r = new ServiceRecord[services.size()];
		services.copyInto(r);
		return r;
	}

	/**
	 * Get the first discovered service from selected remote device.
	 * Your application call this method after your app receives COMPLETED
	 * callback event. This will return the first service that match your
	 * UUIDs in startInquiry().
	 *
	 * @return ServiceRecord null if no service discovered
	 */
	public ServiceRecord getFirstDiscoveredService() {
		if (services.size() > 0)
			return (ServiceRecord) services.elementAt(0);
		else
			return null;
	}

	/**
	 * Return the Bluetooth result code from device inquiry.
	 * This is the result code obtained in  DiscoveryListener.inquiryCompleted().
	 * Your application cal call this method after a COMPLETED callback event
	 * is received.
	 * @return int
	 */
	public int getDeviceDiscoveryReturnCode() {
		return deviceReturnCode;
	}

	/**
	 * Return the Bluetooth result code from service discovery.
	 * This is the result code obtained in  DiscoveryListener.serviceSearchCompleted().
	 * Your application cal call this method after a COMPLETED callback event
	 * is received.
	 * @return int
	 */
	public int getServiceDiscoveryReturnCode() {
		return serviceReturnCode;
	}

	/**
	 * Return user selected remote device that is used for service discovery.
	 * Your application can call this after your app received SELECTED callback
	 * event.
	 * @return RemoteDevice null if user didn't select anything
	 */
	public RemoteDevice getSelectedDevice() {
		if (selectedDevice != -1)
			return (RemoteDevice) devices.elementAt(selectedDevice);
		else
			return null;
	}

	/**
	 * Start device inquiry. Your application call this method to start inquiry.
	 * @param mode int one of DiscoveryAgent.GIAC or DiscoveryAgent.LIAC
	 * @param serviceUUIDs UUID[]
	 */
	public void startInquiry(int mode, UUID[] serviceUUIDs) {
		try {
			this.discoveryMode = mode;
			this.serviceUUIDs = serviceUUIDs;

			// clear previous values first
			devices.removeAllElements();
			deviceClasses.removeAllElements();

			//
			// initialize the JABWT stack
			device = LocalDevice.getLocalDevice(); // obtain reference to singleton
			device.setDiscoverable(DiscoveryAgent.GIAC); // set Discover Mode
			if(agent == null) //NLNN
				agent = device.getDiscoveryAgent(); // obtain reference to singleton
			agent.cancelInquiry(new Listener()); //NLNN
			boolean result = agent.startInquiry(mode, new Listener());
			
		} catch (BluetoothStateException e) {
			e.printStackTrace();
		}

	}

	/**
	 *
	 * @param c Command
	 * @param d Displayable
	 */

	public void searchDevice() {
		startInquiry(discoveryMode, serviceUUIDs);
	}

	public void selectDevice(int kSelected) {
		// get selected device

		// Chon thiet bi tai day
		selectedDevice = kSelected;
		RemoteDevice remoteDevice = (RemoteDevice) devices
				.elementAt(selectedDevice);
		
		// remove all existing record first
		services.removeAllElements();

		try {
			agent.searchServices(null, serviceUUIDs, remoteDevice,
					new Listener());

		} catch (BluetoothStateException ex) {
			ex.printStackTrace();
		}
	}

	/**
	 * Bluetooth listener object.
	 * Register this listener object to DiscoveryAgent in device inqury and service discovery.
	 */
	class Listener implements DiscoveryListener {

		public void deviceDiscovered(RemoteDevice remoteDevice,
				DeviceClass deviceClass) {
			log("A remote bluetooth device is discovered.");
			Util.printRemoteDevice(remoteDevice, deviceClass);
			devices.addElement(remoteDevice);
			deviceClasses.addElement(deviceClass);
		}

		public void inquiryCompleted(int complete) {
			inquiryCompleted = true;
			log("Device discovery is completed with return code: " + complete);
			log("" + devices.size() + " device(s) discovered");

			deviceReturnCode = complete;

			if (devices.size() == 0) {
				log("Can not discover any bluetooth devices.");
			}
			/*
			String[] deviceNames = new String[devices.size()];
			
			try {
				for(int i = 0; i < devices.size(); i ++) {
					RemoteDevice device = (RemoteDevice)devices.elementAt(i);
					deviceNames[i] = device.getFriendlyName(false);
				}
			}
			catch(IOException exception) {
				exception.printStackTrace();
			}
			MapDrawer.getMidlet().showGPSConnectionList(deviceNames);
			*/
		}

		public void servicesDiscovered(int transId, ServiceRecord[] records) {
			// note: we do not use transId because we only have one search at a time
			log("Remote bluetooth services are discovered.");
			for (int i = 0; i < records.length; i++) {
				ServiceRecord record = records[i];
				Util.printServiceRecord(record);
				services.addElement(record);
			}
		}

		public void serviceSearchCompleted(int transId, int complete) {
			// note: we do not use transId because we only have one search at a time
			log("Service discovery completed with return code:" + complete);
			log("" + services.size() + " service(s) discovered");

			serviceReturnCode = complete;
			
			//Display.getDisplay(MapDrawer.getMidlet()).callSerially( new Worker( ID_SERVICE_COMPLETED ) );
		}

	} // Listener

	private final static int ID_SERVICE_COMPLETED = 1;
	private final static int ID_DEVICE_COMPLETED = 2;
	private final static int ID_DEVICE_SELECTED = 3;

	/**
	 * Worker thread that invoke callback CommandListener upon Bluetooth event occurs.
	 */
	class Worker implements Runnable {
		int cmd = 0;

		public Worker(int cmd) {
			this.cmd = cmd;
		}

		public void run() {
			switch (cmd) {
			case ID_SERVICE_COMPLETED:
				//Do something
				//MapDrawer.getMidlet().showMapCanvas();
				break;
			case ID_DEVICE_COMPLETED:
				//Do something
				break;
			case ID_DEVICE_SELECTED:
				//Do something
				break;
			default:
				break;
			}
		}
	}

	public Vector getDevices() {
		return devices;
	}

	public Vector getServices() {
		return services;
	}

	public boolean isInquiryCompleted() {
		return inquiryCompleted;
	}

}
